#!/usr/bin/env python3

import os
import shlex
import subprocess
import threading

from shell_helpers import LF
import common
import thread_pool

class Main(common.BuildCliFunction):
    def __init__(self, *args, **kwargs):
        if not 'description' in kwargs:
            kwargs['description'] = '''\
Build our compiled userland examples.
'''
        if not 'defaults' in kwargs:
            kwargs['defaults'] = {}
        if not 'mode' in kwargs['defaults']:
            kwargs['defaults']['mode'] = 'userland'
        super().__init__(*args, **kwargs)
        self._add_argument('--ccflags')
        self._add_argument('--force-rebuild')
        self._add_argument('--optimization-level')
        self.add_argument(
            '--out-rootfs-overlay-dir-prefix',
            default='',
            help='''\
Place the output files inside the image within this additional prefix.
This is mostly useful to place different versions of binaries with different
build parameters inside image to compare them. See:
* https://cirosantilli.com/linux-kernel-module-cheat#update-the-toolchain
* https://cirosantilli.com/linux-kernel-module-cheat#out_rootfs_overlay_dir
'''
        )
        self.add_argument(
            'targets',
            default=[],
            help='''\
Select to build only the given userland programs, or all programs under
the given directories.

Default: build all.

Must point to either sources or directories under userland/, or to LKMC
toplevel which is a synonym for userland/.

Default: build all examples that have their package dependencies met, e.g.:

-   userland/arch/ programs only build if the target arch matches
-   an OpenBLAS example can only be built if the target root filesystem
    has the OpenBLAS libraries and headers installed, which you must inform
    with --package
''',
            nargs='*',
        )

    def build(self):
        build_dir = self.get_build_dir()
        cc_flags = [
            '-I', self.env['root_dir'], LF,
            '-O{}'.format(self.env['optimization_level']), LF,
        ] + self.sh.shlex_split(self.env['ccflags'])
        if self.env['static']:
            cc_flags.extend(['-static', LF])
        extra_obj_lkmc_common = os.path.join(
            build_dir,
            self.env['common_basename_noext'] + self.env['obj_ext']
        )
        self._build_one(
            in_path=self.env['common_c'],
            out_path=extra_obj_lkmc_common,
            cc_flags=cc_flags,
            extra_deps=[self.env['common_h']],
            link=False,
        )
        with thread_pool.ThreadPool(
            self._build_one,
            nthreads=self.env['nproc'],
            submit_raise_exit=self.env['quit_on_fail'],
        ) as my_thread_pool:
            for target in self.env['targets']:
                for path, in_dirnames, in_filenames in self.sh.walk(target):
                    for in_filename in in_filenames:
                        in_ext = os.path.splitext(in_filename)[1]
                        if not in_ext in self.env['build_in_exts']:
                            continue
                        in_path = os.path.join(path, in_filename)
                        my_thread_pool.submit({
                            'cc_flags': cc_flags,
                            'extra_objs_lkmc_common': [extra_obj_lkmc_common],
                            'in_path': in_path,
                            'out_path': self.resolve_userland_executable(in_path),
                        })
        exit_status = self._handle_thread_pool_errors(my_thread_pool)
        if exit_status != 0:
            return exit_status
        if not self.env['in_tree']:
            self.sh.copy_dir_if_update(
                srcdir=build_dir,
                destdir=os.path.join(
                    self.env['out_rootfs_overlay_lkmc_dir'],
                    self.env['out_rootfs_overlay_dir_prefix']
                ),
                filter_ext=self.env['userland_executable_ext'],
            )
        return exit_status

    def clean(self):
        if self.env['in_tree']:
            for target in self.env['targets']:
                if os.path.exists(target):
                    if os.path.isfile(target):
                        self.sh.rmrf(self.resolve_userland_executable(target))
                    else:
                        for path, dirnames, filenames in self.sh.walk(target):
                            for filename in filenames:
                                if os.path.splitext(filename)[1] in self.env['userland_out_exts']:
                                    self.sh.rmrf(os.path.join(path, filename))
        else:
            for target in self.env['targets']:
                self.sh.rmrf(self.resolve_userland_executable(target))

    def get_build_dir(self):
        return self.env['userland_build_dir']

    def setup_one(self):
        self.env['targets'] = self.resolve_targets(
            [self.env['userland_source_dir']],
            self.env['targets']
        )

if __name__ == '__main__':
    Main().cli()
